#include <mysql.h>
#include "cgzCMysql.h"
#include "cgzCThreadPool.h"

cgzCMysqlReply::cgzCMysqlReply(cgzMysqlCode code)
    : m_code(code)
{
}

cgzCMysqlReply::~cgzCMysqlReply()
{
}

cgzCMysqlExec::cgzCMysqlExec(const cgzCMysqlCmd cmd, const char *sql, const int cookid)
    : cgzCMysqlReply(cgzMysqlCode::EXEC_REQUEST),
      m_cmd(cmd),
      m_sql(sql),
      m_cookid(cookid),
      m_ret(false),
      m_err(false),
      m_sort(-1),
      m_reply(nullptr)
{
}

cgzCMysqlExec::~cgzCMysqlExec()
{
    m_blobs.clear();
    if (nullptr != m_reply)
    {
        mysql_free_result(m_reply);
    }
}

bool cgzMysqlPingCheck(MYSQL *c)
{
    if (!c)
    {
        return false; // 根本没有连接
    }
    else if (mysql_ping(c))
    {
        return false; // ping不通了
    }
    return true; // 连接有效
}

void cgzMysqlCloseCheck(MYSQL **c)
{
    if (*c)
    {
        mysql_close(*c);
    }
    *c = nullptr;
}

cgzCMysql::cgzCMysql(
    cgzCThreadPool *pool,
    const char *name,
    const char *ip,
    const char *user,
    const char *password,
    const int port,
    const char *db,
    const char *charset,
    const int cid)
    : cgzITask(pool, name, cgzETask::CMysql),
      m_ip(ip),
      m_user(user),
      m_password(password),
      m_db(db),
      m_charset(charset),
      m_firstAuth(false),
      m_port(port),
      m_cid(cid),
      m_mysql(nullptr)

{
    this->connect();
    // 注册服务
    service();
}

cgzCMysql::~cgzCMysql()
{
}

void cgzCMysql::authret()
{
    if (!m_firstAuth)
    {
        m_firstAuth = true;
        auto node = malloc(sizeof(cgzCMysqlReply));
        auto acptC = new (node) cgzCMysqlReply(cgzMysqlCode::AUTH_SUCCESS);
        acptC->target = m_cid;
        acptC->origin = getId();
        retTask(acptC);
    }
}

bool cgzCMysql::connect()
{
    m_mysql = mysql_init(NULL);

    // 创建连接
    if (nullptr != mysql_real_connect(
                       m_mysql,
                       m_ip.c_str(),
                       m_user.c_str(),
                       m_password.c_str(),
                       m_db.c_str(),
                       m_port,
                       nullptr, 0))
    {
        mysql_set_character_set(m_mysql, m_charset.c_str());
        mysql_set_character_set(m_mysql, m_charset.c_str());
        cgz_this_error("Connect to mysql success! name:{}", m_name);
    }
    else
    {
        cgz_this_error("Connect to mysql failed! name:{}", m_name);
        cgzMysqlCloseCheck(&m_mysql);
        return false;
    }

    authret();
    return true;
}

bool cgzCMysql::rconnect()
{
    // 检查mysql连接状态
    bool disc = false;
    if (!cgzMysqlPingCheck(m_mysql))
    {
        disc = true; // 没有连接成功
    }

    // 检测mysql释放处理
    cgzMysqlCloseCheck(&m_mysql);

    if (disc)
    {
        if (!this->connect())
        {
            cgz_this_error("mysql reconnect fail! name:{}", m_name);
            return false; // 重连失败
        }
        cgz_this_error("mysql reconnect succ! name:{}", m_name);
    }
    // 返回连接成功
    return true;
}

bool cgzCMysql::run(int64_t nms)
{
    MYSQL_BIND bind[128];
    bool no = false;

    while (!m_runTask.empty())
    {
        auto cmdTask = static_cast<cgzCMysqlExec *>(m_runTask.front());
        auto &sql = cmdTask->m_sql;
        int binCount = cmdTask->m_blobs.size();

        bool ok = true;
        if (no)
        {
            ok = false;
            // 判断网络无法重连成功
        }
        else if (!m_mysql)
        {
            ok = false;
            if (rconnect())
            {
                // 重新连接成功了
                continue;
            }
            else
            {
                no = true;
                // 无法重连成了
            }
        }
        else if (binCount > 0)
        {
            // 初始准备
            MYSQL_STMT *stmt = mysql_stmt_init(m_mysql);
            if (stmt == NULL)
            {
                ok = false;
                auto err = mysql_stmt_error(stmt);
                cgz_this_error("mysql_stmt_init failed! name:{} sql:{} err:{}", m_name, sql, err);
                break;
            }

            // 解析命令
            if (mysql_stmt_prepare(stmt, sql.c_str(), sql.length()))
            {
                ok = false;
                auto err = mysql_stmt_error(stmt);
                cgz_this_error("mysql_stmt_prepare failed! name:{} sql:{} err:{}", m_name, sql, err);
                mysql_stmt_close(stmt);
                break;
            }

            int param_count = mysql_stmt_param_count(stmt);
            if (param_count != binCount)
            {
                ok = false;
                auto err = mysql_stmt_error(stmt);
                cgz_this_error("mysql_stmt_prepare failed! name:{} sql:{} err:{}", m_name, sql, err);
                mysql_stmt_close(stmt);
                break;
            }

            // 绑定参数
            memset(bind, 0, sizeof(bind[0]) * binCount);
            for (int i = 0; i < binCount; i++)
            {
                auto &str = cmdTask->m_blobs[i];
                bind[i].buffer_type = MYSQL_TYPE_BLOB;
                bind[i].buffer = (void *)str.data();
                bind[i].buffer_length = str.size();
                bind[i].is_null = 0;
                bind[i].length = 0;
            }

            // 绑定参数
            if (mysql_stmt_bind_param(stmt, bind))
            {
                ok = false;
                auto err = mysql_stmt_error(stmt);
                cgz_this_error("mysql_stmt_bind_param failed! name:{} sql:{} err:{}", m_name, sql, err);
                mysql_stmt_close(stmt);
                break;
            }

            // 执行命令
            if (mysql_stmt_execute(stmt))
            {
                ok = false;
                auto err = mysql_stmt_error(stmt);
                cgz_this_error("mysql_stmt_execute failed! name:{} sql:{} err:{}", m_name, sql, err);
                mysql_stmt_close(stmt);
                break;
            }

            // 获取结果
            auto rows = mysql_stmt_affected_rows(stmt);
            // auto insert_id = mysql_stmt_insert_id(stmt);
            if (rows <= 0)
            {
                ok = false;
                auto err = mysql_stmt_error(stmt);
                cgz_this_error("mysql_stmt_affected_rows failed! name:{} sql:{} affected_rows={}", m_name, sql, rows, err);
                mysql_stmt_close(stmt);
                break;
            }

            // 释放资源
            mysql_stmt_close(stmt);
            cmdTask->m_reply = nullptr;
        }
        else
        {
            // 处理非BLOB数据
            int rst = mysql_query(m_mysql, sql.c_str());
            if (rst)
            {
                ok = false;
                auto err = mysql_error(m_mysql);
                if (err && strlen(err) > 0)
                {
                    // 打印错误日志
                    cgz_this_error("mysql_query failed! name:{} sql:{} err:{}", m_name.c_str(), sql.c_str(), err);
                }
            }
            else
            {
                // 执行成功
                MYSQL_RES *reply = mysql_store_result(m_mysql);
                cmdTask->m_reply = reply;
            }
        }

        if ((!ok) && (!no))
        {
            // 失败&&连接成功过--判断是否断开连接
            if (!cgzMysqlPingCheck(m_mysql))
            {
                cgzMysqlCloseCheck(&m_mysql);
                continue; // 连接失败再次尝试
            }
        }

        cmdTask->m_err = !ok;
        m_runTask.pop_front();

        if (cmdTask->m_ret)
        {
            // 插入标识
            if (cmdTask->m_cmd == cgzCMysqlCmd::INSERT)
            {
                cmdTask->m_sort = mysql_insert_id(m_mysql);
            }
            // 回复请求
            cmdTask->origin = this->m_id;
            cmdTask->target = this->m_cid;
            cmdTask->m_code = cgzMysqlCode::EXEC_REPLY;
            retTask(cmdTask);
        }
        else
        {
            // 释放处理
            cmdTask->~cgzCMysqlExec();
            free(cmdTask);
        }
    }

    return true;
}

bool cgzCMysql::addTask(cgzIData *cgzTask)
{
    if (m_runTask.size() > 100)
    {
        return false;
    }
    else
    {
        return cgzITask::addTask(cgzTask);
    }
}
